package com.todo.auth.service;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import com.alibaba.fastjson.JSONObject;
import com.todo.admin.common.base.SystemLoginUser;
import com.todo.auth.conf.TodoAuthBaseConfig;
import com.todo.auth.controller.request.StaffLoginRequest;
import com.todo.auth.controller.response.CaptchaResponse;
import com.todo.auth.remote.StaffRemoteService;
import com.todo.auth.remote.dto.*;
import com.todo.auth.tool.TokenService;
import com.todo.common.common.constants.Constants;
import com.todo.common.common.entity.response.BizResponse;
import com.todo.common.common.enums.BizErrorCodeEnum;
import com.todo.common.common.exception.BizException;
import com.todo.component.redis.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author azhebuxing
 * @date 2025/2/20 01:50
 * @description
 */
@Slf4j
@Service
public class StaffLoginService {

    @Resource
    private StaffRemoteService staffRemoteService;

    @Resource
    private TokenService tokenService;

    @Resource
    private RedisService redisService;

    @Resource
    private AuthenticationManager authenticationManager;

    public CaptchaResponse getCaptchaImg() {
        BizResponse<CaptchaDTO> captchaResponse = staffRemoteService.getCaptchaImg();
        if (captchaResponse == null || captchaResponse.getData() == null || !captchaResponse.getData().getIsCaptchaOn()) {
            CaptchaResponse response = new CaptchaResponse();
            response.setIsCaptchaOn(false);
            return response;
        }
        CaptchaDTO captchaDTO = captchaResponse.getData();

        String logKey = String.format(Constants.LOGIN_CAPTCHA_KEY, captchaDTO.getCaptchaCodeKey());
        redisService.set(logKey, captchaDTO.getAnswer());
        CaptchaResponse response = new CaptchaResponse();
        response.setIsCaptchaOn(captchaDTO.getIsCaptchaOn());
        response.setCaptchaCodeKey(captchaDTO.getCaptchaCodeKey());
        response.setCaptchaCodeImg(captchaDTO.getCaptchaCodeImg());
        return response;
    }

    public StaffDetailDTO queryStaffDetailByUserName(String username) {
        StaffUserNameDTO userNameDTO = new StaffUserNameDTO();
        userNameDTO.setUsername(username);
        BizResponse<StaffDetailDTO> staffResponse = staffRemoteService.queryStaffDetailByUserName(userNameDTO);
        if (staffResponse == null || staffResponse.getData() == null) {
            throw new BizException(BizErrorCodeEnum.PROCESS_FAIL);
        }
        return staffResponse.getData();
    }

    public StaffInfoDTO queryStaffInfoById(Long userId) {
        StaffUserIdDTO userIdDTO = new StaffUserIdDTO();
        userIdDTO.setUserId(userId);
        BizResponse<StaffInfoDTO> staffResponse = staffRemoteService.queryUserInfoById(userIdDTO);
        if (staffResponse == null || staffResponse.getData() == null) {
            throw new BizException(BizErrorCodeEnum.PROCESS_FAIL);
        }
        return staffResponse.getData();
    }


    /**
     * 登录验证
     *
     * @param request 登录参数
     * @return 结果
     */
    public String loginAndGetToken(StaffLoginRequest request) {
        // 验证码开关，当前默认关闭
        if (false) {
            validateCaptcha(request.getUsername(), request.getCaptchaCode(), request.getCaptchaCodeKey());
        }
        // 用户验证
        Authentication authentication;
        String decryptPassword = decryptPassword(request.getPassword());
        try {
            // 该方法会去调用UserDetailsServiceImpl#loadUserByUsername  校验用户名和密码  认证鉴权
            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
                    request.getUsername(), decryptPassword));
        } catch (BadCredentialsException e) {
            log.error("StaffLoginService#loginAndGetToken 用户登录失败，账号密码异常 request:【{}】", JSONObject.toJSONString(request));
            throw new BizException(BizErrorCodeEnum.PASSWORD_ERROR, e.getMessage());
        } catch (Exception e) {
            log.error("StaffLoginService#loginAndGetToken 用户登录失败，系统异常 request:【{}】", JSONObject.toJSONString(request));
            throw new BizException(BizErrorCodeEnum.PROCESS_FAIL, e.getMessage());
        }
        // 把当前登录用户 放入上下文中
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 这里获取的loginUser是UserDetailsServiceImpl#loadUserByUsername方法返回的LoginUser
        SystemLoginUser loginUser = (SystemLoginUser) authentication.getPrincipal();
        RecordLoginInfoDTO recordLoginInfoDTO = new RecordLoginInfoDTO();
        recordLoginInfoDTO.setUsername(loginUser.getUsername());
        recordLoginInfoDTO.setUserId(loginUser.getUserId());
        staffRemoteService.recordLoginInfo(recordLoginInfoDTO);
        // 生成token
        return tokenService.createTokenAndPutUserInCache(loginUser);
    }

    private String decryptPassword(String originalPassword) {
        byte[] decryptBytes = SecureUtil.rsa(TodoAuthBaseConfig.getRsaPrivateKey(), null)
                .decrypt(Base64.decode(originalPassword), KeyType.PrivateKey);

        return StrUtil.str(decryptBytes, CharsetUtil.CHARSET_UTF_8);
    }

    /**
     * 校验验证码
     *
     * @param username       用户名
     * @param captchaCode    验证码
     * @param captchaCodeKey 验证码对应的缓存key
     */
    private void validateCaptcha(String username, String captchaCode, String captchaCodeKey) {
        String logKey = String.format(Constants.LOGIN_CAPTCHA_KEY, captchaCodeKey);
        String captcha = redisService.get(logKey);
        redisService.del(logKey);
        if (captcha == null) {
            log.error("StaffLoginService#validateCaptcha 用户登录失败，校验验证码异常 username：【{}】，captchaCodeKey:【{}】", username, captchaCodeKey);
            throw new BizException(BizErrorCodeEnum.CAPTCHA_ERROR);
        }
        if (!captchaCode.equalsIgnoreCase(captcha)) {
            log.error("StaffLoginService#validateCaptcha 用户登录失败，校验验证码错误 username：【{}】，captchaCodeKey:【{}】", username, captchaCodeKey);
            throw new BizException(BizErrorCodeEnum.CAPTCHA_ERROR);
        }
    }

}
